Skip to content

feat(reconcile): add a Preference kind#1762

Merged
rohilsurana merged 5 commits into
mainfrom
feat/reconcile-preference-kind
Jul 16, 2026
Merged

feat(reconcile): add a Preference kind#1762
rohilsurana merged 5 commits into
mainfrom
feat/reconcile-preference-kind

Conversation

@rohilsurana

Copy link
Copy Markdown
Member

Stacked on #1737; will rebase onto main once #1731 and #1737 squash-merge.

What

A Preference kind for frontier reconcile and frontier export, for platform settings
like disable_orgs_on_create and the invite mail template.

kind: Preference
spec:
  - name: disable_orgs_on_create
    value: "true"

How it behaves (per the RFC's rules)

  • Missing entry resets to default (rule 4, the settings option). The file is the full
    desired state. A preference you list is set to your value; a preference you leave out is
    written back to its trait default. This is the first kind to use reset-to-default.
  • No delete flag (rule 5). There is no delete RPC, and setting a preference back to
    its default is what removal means here.
  • Export (rule 9). Export writes only the preferences whose value differs from the
    default, sorted by name, so reconciling an export plans no changes.

Values are strings end to end, so a yes/no setting compares as "true" or "false".
Unknown names and duplicate names fail the plan. A stored value whose trait no longer
exists is left alone — the file cannot name it, so nothing manages it.

API use

  • ListPreferences (AdminService) reads the stored platform preferences.
  • DescribePreferences (FrontierService) gives the valid platform traits and their
    defaults — both the source of defaults and the set of valid names.
  • CreatePreferences (AdminService) upserts a value; a reset writes the default back.

Values are read from the DB on every call, so a write reaches all pods. There is a
standing TODO in core/preference/service.go to cache platform preferences; if that lands
as a boot-lifetime map, pods would serve stale values after a write. It needs a TTL or
read-through. Flagging it here so the cache is not added without that.

Tests

  • Diff: set, reset, converged, a value equal to the default is a no-op, sets before
    resets, unknown trait, duplicate name, empty name, and a stored value whose trait is
    gone.
  • Reconciler with a fake API: apply a set and a reset, dry run, unknown trait fails before
    applying, export returns only overrides and round-trips, and an all-default export is
    empty.

@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Jul 16, 2026 9:56am

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for reconciling platform preferences through desired-state files.
    • Preferences can be set or reset to their defaults, including when omitted from the file.
    • Added preference support to CLI reconciliation and export commands.
    • Exports include only preferences that differ from their defaults.
  • Documentation

    • Documented preference YAML format, validation rules, reset behavior, and export semantics.
    • Clarified reconciliation and deletion behavior in the CLI reference.

Walkthrough

Adds platform preferences as a declarative reconcile and export kind, including validation, default-reset semantics, dry-run and apply behavior, non-default export filtering, CLI registration, documentation, and test coverage.

Changes

Preference reconciliation

Layer / File(s) Summary
Preference diff planning
internal/reconcile/preference.go, internal/reconcile/preference_test.go
Defines preference specs and validates names, duplicates, values, defaults, set operations, reset operations, and ordering.
Preference reconciler and export
internal/reconcile/preference_reconciler.go, internal/reconcile/preference_reconciler_test.go
Fetches current preferences and platform defaults, supports dry-run and apply modes, exports only non-default values, and tests apply, validation, and round-trip behavior.
CLI registration and documentation
cmd/reconcile.go, docs/content/docs/reconcile.mdx, docs/content/docs/reference/cli.mdx
Registers Preference for reconciliation and documents its YAML format, omission semantics, and export support.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested reviewers: whoabhisheksah

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coveralls

coveralls commented Jul 15, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 29488954811

Coverage increased (+0.1%) to 45.987%

Details

  • Coverage increased (+0.1%) from the base build.
  • Patch coverage: 16 uncovered changes across 2 files (111 of 127 lines covered, 87.4%).
  • 1 coverage regression across 1 file.

Uncovered Changes

File Changed Covered %
internal/reconcile/preference_reconciler.go 78 63 80.77%
cmd/reconcile.go 7 6 85.71%
Total (3 files) 127 111 87.4%

Coverage Regressions

1 previously-covered line in 1 file lost coverage.

File Lines Losing Coverage Coverage
cmd/reconcile.go 1 44.05%

Coverage Stats

Coverage Status
Relevant Lines: 38426
Covered Lines: 17671
Line Coverage: 45.99%
Coverage Strength: 13.29 hits per line

💛 - Coveralls

@rohilsurana rohilsurana marked this pull request as ready for review July 16, 2026 07:50
@rohilsurana rohilsurana force-pushed the feat/reconcile-roles-permissions branch from 71e95c2 to a226f47 Compare July 16, 2026 08:28
@rohilsurana rohilsurana force-pushed the feat/reconcile-preference-kind branch from 59b3ac4 to aa8a7e4 Compare July 16, 2026 08:30
@rohilsurana rohilsurana force-pushed the feat/reconcile-preference-kind branch from aa8a7e4 to 8256165 Compare July 16, 2026 09:43
Base automatically changed from feat/reconcile-roles-permissions to main July 16, 2026 09:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
internal/reconcile/preference_test.go (1)

86-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover an omitted preference stored as empty.

Add a case with current[name] == "" and an empty desired list. It should produce no reset because the server already resolves that value to the default.

internal/reconcile/preference_reconciler_test.go (1)

108-137: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover empty stored values during export.

Add a preference with Value: "" and a non-empty trait default, then assert it is omitted and the exported document round-trips without planned operations.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 870b9a80-10e5-4541-a4d5-40325a7ed505

📥 Commits

Reviewing files that changed from the base of the PR and between 3c08b2f and c094c3e.

📒 Files selected for processing (7)
  • cmd/reconcile.go
  • docs/content/docs/reconcile.mdx
  • docs/content/docs/reference/cli.mdx
  • internal/reconcile/preference.go
  • internal/reconcile/preference_reconciler.go
  • internal/reconcile/preference_reconciler_test.go
  • internal/reconcile/preference_test.go

Comment on lines +71 to +89
var sets, resets []preferenceOp
for name, value := range desiredByName {
if value != serverValue(name) {
sets = append(sets, preferenceOp{action: opSet, name: name, value: value})
}
}
for name, value := range current {
def, known := defaults[name]
if !known {
// A stored value whose trait no longer exists: leave it alone. The
// file cannot name it (unknown names fail), so nothing manages it.
continue
}
if _, listed := desiredByName[name]; listed {
continue
}
if value != def {
resets = append(resets, preferenceOp{action: opReset, name: name, value: def})
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Normalize empty stored values consistently.

The planner recognizes "" as unset/default, but reset planning and export compare the raw value. This causes unnecessary resets and exports that do not round-trip cleanly.

  • internal/reconcile/preference.go#L71-L89: compare serverValue(name) with the default when deciding resets.
  • internal/reconcile/preference_reconciler.go#L85-L92: omit empty stored values from export.
  • internal/reconcile/preference_test.go#L86-L95: test that an omitted empty stored preference produces no operation.
  • internal/reconcile/preference_reconciler_test.go#L108-L137: test that empty stored values are omitted and round-trip without changes.
📍 Affects 4 files
  • internal/reconcile/preference.go#L71-L89 (this comment)
  • internal/reconcile/preference_reconciler.go#L85-L92
  • internal/reconcile/preference_test.go#L86-L95
  • internal/reconcile/preference_reconciler_test.go#L108-L137

@rohilsurana rohilsurana merged commit 7dcfcee into main Jul 16, 2026
8 checks passed
@rohilsurana rohilsurana deleted the feat/reconcile-preference-kind branch July 16, 2026 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants